<?php

class Pageley_Gateway_Database_BasicTableTests extends Pageley_Test_Case {
    private $table;
    private $gateway;
    private $mockFactory;
    private $connection;
    private $transaction;
    private $statement;
    private $resultSet;
    
    public function setUp() {
        $this->mockFactory = new Pageley_Mock_Factory();
        $this->gateway = $this->mockFactory->createMock(new Pageley_Util_ArrayMap("Pageley_Gateway"));
        $string = new Pageley_Domain_Attribute_Basic("string");
		$number = new Pageley_Domain_Attribute_Basic("number");
		$namespace = new Pageley_Domain_BasicNamespace("urn:commerce.pageley.org:1.0", "commerce", "commerce");
        $this->type = new Pageley_Domain_BasicType(
			$namespace,
			"Article",
			new Pageley_Util_ArrayMap(array(
				"name" => $string,
				"price" => $number
			))
		);
        $this->table = new Pageley_Gateway_Database_BasicTable($this->type, $this->gateway);
        $this->connection = $this->mockFactory->createMock(new Pageley_Util_ArrayMap("Pageley_Gateway_Database_Connection"));
        $this->transaction = $this->mockFactory->createMock(new Pageley_Util_ArrayMap("Pageley_Gateway_Database_Transaction"));
        $this->statement = $this->mockFactory->createMock(new Pageley_Util_ArrayMap("Pageley_Gateway_Database_Statement"));
        $this->resultSet = $this->mockFactory->createMock(new Pageley_Util_ArrayMap("Pageley_Gateway_Database_ResultSet"));
    }
    
    public function tearDown() {
        $this->mockFactory->finish();
    }
    
    public function testFind() {
        // Prepare scenario
        $this->gateway->getTransaction(true, true)->andReturn($this->transaction);
        $this->transaction->getConnection()->andReturn($this->connection);
        $this->connection->prepareStatement("SELECT a1.\"article_id\", a1.\"name\", a1.\"price\"" .
            " FROM \"commerce\".\"article\" AS a1" . 
            " WHERE a1.\"article_id\" = :article_id")->andReturn($this->statement);
        $this->statement->execute(new Pageley_Util_ArrayMap(array("article_id" => 1)))->andReturn($this->resultSet);
        $this->resultSet->hasNext()->andReturn(true);
        $this->resultSet->next();
        $this->resultSet->get("name")->andReturn("Llama spray");
        $this->resultSet->get("price")->andReturn(5.5);
        $this->resultSet->get("article_id")->andReturn(1);
        $this->resultSet->hasNext()->andReturn(false);
        $this->transaction->getSingleStatement()->andReturn(false);
        
        // Replay
        $this->mockFactory->replay();
        $entity = $this->table->find(1);
        $this->assertNotNull($entity);
        $this->assertEquals(1, $entity->getId());
        $this->assertEquals("Llama spray", $entity->get("name"));
        $this->assertEquals(5.5, $entity->get("price"));
    }
    
    public function testFindNothing() {
        // Prepare scenario
        $this->gateway->getTransaction(true, true)->andReturn($this->transaction);
        $this->transaction->getConnection()->andReturn($this->connection);
        $this->connection->prepareStatement("SELECT a1.\"article_id\", a1.\"name\", a1.\"price\"" .
            " FROM \"commerce\".\"article\" AS a1" . 
            " WHERE a1.\"article_id\" = :article_id")->andReturn($this->statement);
        $this->statement->execute(new Pageley_Util_ArrayMap(array("article_id" => 1)))->andReturn($this->resultSet);
        $this->resultSet->hasNext()->andReturn(false);
        $this->transaction->getSingleStatement()->andReturn(false);
        
        // Replay
        $this->mockFactory->replay();
        $entity = $this->table->find(1);
        $this->assertNull($entity);
    }
    
    public function testFindMultiple() {
        // Prepare scenario
        $this->gateway->getTransaction(true, true)->andReturn($this->transaction);
        $this->transaction->getConnection()->andReturn($this->connection);
        $this->connection->prepareStatement("SELECT a1.\"article_id\", a1.\"name\", a1.\"price\"" .
            " FROM \"commerce\".\"article\" AS a1" . 
            " WHERE a1.\"article_id\" = :article_id")->andReturn($this->statement);
        $this->statement->execute(new Pageley_Util_ArrayMap(array("article_id" => 1)))->andReturn($this->resultSet);
        $this->resultSet->hasNext()->andReturn(true);
        $this->resultSet->next();
        $this->resultSet->get("name")->andReturn("Llama spray");
        $this->resultSet->get("price")->andReturn(5.5);
        $this->resultSet->get("article_id")->andReturn(1);
        $this->resultSet->hasNext()->andReturn(true);
        $this->resultSet->next();
        $this->resultSet->get("name")->andReturn("Frog spray");
        $this->resultSet->get("price")->andReturn(2.5);
        $this->resultSet->get("article_id")->andReturn(2);
        $this->resultSet->hasNext()->andReturn(false);
        $this->transaction->getSingleStatement()->andReturn(false);
        
        // Replay
        $this->mockFactory->replay();
        try {
            $entity = $this->table->find(1);
            $this->fail("Expected Pageley_Gateway_Database_Exception");
        } catch(Pageley_Gateway_Database_Exception $e) {
            $this->assertEquals("Find by ID on table \"commerce\".\"article\" returned more than one record.", $e->getMessage());
        }
    }
    
    public function testRollbackOnFailure() {
        // Prepare scenario
        $this->gateway->getTransaction(true, true)->andReturn($this->transaction);
        $this->transaction->getConnection()->andReturn($this->connection);
        $this->connection->prepareStatement("SELECT a1.\"article_id\", a1.\"name\", a1.\"price\"" .
            " FROM \"commerce\".\"article\" AS a1" . 
            " WHERE a1.\"article_id\" = :article_id")->andReturn($this->statement);
        $this->statement->execute(new Pageley_Util_ArrayMap(array("article_id" => 1)))->andThrow(new Pageley_Gateway_Database_BadSqlException("Bad SQL!"));
        $this->transaction->rollback();
        
        // Replay
        $this->mockFactory->replay();
        try {
            $entity = $this->table->find(1);
            $this->fail("Expected Pageley_Gateway_Database_BadSqlException");
        } catch(Pageley_Gateway_Database_BadSqlException $e) {
            $this->assertEquals("Bad SQL!", $e->getMessage());
        }
    }
    
    public function testCommitSingleStatement() {
        // Prepare scenario
        $this->gateway->getTransaction(true, true)->andReturn($this->transaction);
        $this->transaction->getConnection()->andReturn($this->connection);
        $this->connection->prepareStatement("SELECT a1.\"article_id\", a1.\"name\", a1.\"price\"" .
            " FROM \"commerce\".\"article\" AS a1" . 
            " WHERE a1.\"article_id\" = :article_id")->andReturn($this->statement);
        $this->statement->execute(new Pageley_Util_ArrayMap(array("article_id" => 1)))->andReturn($this->resultSet);
        $this->resultSet->hasNext()->andReturn(true);
        $this->resultSet->next();
        $this->resultSet->get("name")->andReturn("Llama spray");
        $this->resultSet->get("price")->andReturn(5.5);
        $this->resultSet->get("article_id")->andReturn(1);
        $this->resultSet->hasNext()->andReturn(false);
        $this->transaction->getSingleStatement()->andReturn(true);
        $this->transaction->commit();
        
        // Replay
        $this->mockFactory->replay();
        $entity = $this->table->find(1);
        $this->assertNotNull($entity);
        $this->assertEquals(1, $entity->getId());
        $this->assertEquals("Llama spray", $entity->get("name"));
        $this->assertEquals(5.5, $entity->get("price"));
    }
    
    public function testQuery() {
        // Prepare scenario
        $query = $this->mockFactory->createMock(new Pageley_Util_ArrayMap("Pageley_Gateway_Database_QuerySqlDecorator"));
        $query->toSql()->andReturn("SELECT a1.\"article_id\", a1.\"name\", a1.\"price\"" .
            " FROM \"commerce\".\"article\" AS a1" . 
            " WHERE a1.\"name\" LIKE :name");
        $query->getValues()->andReturn(new Pageley_Util_ArrayMap(array("name" => '%spray%')));
        $this->gateway->getTransaction(true, true)->andReturn($this->transaction);
        $this->transaction->getConnection()->andReturn($this->connection);
        $this->connection->prepareStatement("SELECT a1.\"article_id\", a1.\"name\", a1.\"price\"" .
            " FROM \"commerce\".\"article\" AS a1" . 
            " WHERE a1.\"name\" LIKE :name")->andReturn($this->statement);
        $this->statement->execute(new Pageley_Util_ArrayMap(array("name" => "%spray%")))->andReturn($this->resultSet);
        $this->resultSet->hasNext()->andReturn(true);
        $this->resultSet->next();
        $this->resultSet->get("name")->andReturn("Llama spray");
        $this->resultSet->get("price")->andReturn(5.5);
        $this->resultSet->get("article_id")->andReturn(1);
        $this->resultSet->hasNext()->andReturn(true);
        $this->resultSet->next();
        $this->resultSet->get("name")->andReturn("Frog spray");
        $this->resultSet->get("price")->andReturn(2.5);
        $this->resultSet->get("article_id")->andReturn(2);
        $this->resultSet->hasNext()->andReturn(false);
        $this->transaction->getSingleStatement()->andReturn(false);
        
        // Replay
        $this->mockFactory->replay();
        $results = $this->table->query($query);
        $this->assertNotNull($results);
        $this->assertEquals(2, $results->size());
        $entity = $results->get(0);
        $this->assertEquals(1, $entity->getId());
        $this->assertEquals("Llama spray", $entity->get("name"));
        $this->assertEquals(5.5, $entity->get("price"));
        $entity = $results->get(1);
        $this->assertEquals(2, $entity->getId());
        $this->assertEquals("Frog spray", $entity->get("name"));
        $this->assertEquals(2.5, $entity->get("price"));
    }
    
    public function testInsert() {
        // Prepare scenario
        $this->gateway->generateId($this->table)->andReturn(1);
        $this->gateway->getTransaction(true, true)->andReturn($this->transaction);
        $this->transaction->getConnection()->andReturn($this->connection);
        $this->connection->prepareStatement("INSERT INTO \"commerce\".\"article\" " .
            "( \"article_id\", \"name\", \"price\" ) " .
            "VALUES ( :article_id, :name, :price )")->andReturn($this->statement);
        $this->statement->execute(new Pageley_Util_ArrayMap(array(
            "name" => "Llama spray",
            "price" => 5.5,
            "article_id" => 1)));
        $this->transaction->getSingleStatement()->andReturn(false);
        
        // Replay
        $this->mockFactory->replay();
        $entity = $this->type->newInstance();
        $entity->set("name", "Llama spray");
        $entity->set("price", 5.5);
        $entity = $this->table->insert($entity);
    }
    
    public function testUpdate() {
        // Prepare scenario
        $this->gateway->getTransaction(true, true)->andReturn($this->transaction);
        $this->transaction->getConnection()->andReturn($this->connection);
        $this->connection->prepareStatement("UPDATE \"commerce\".\"article\" " .
            "SET \"name\" = :name, " .
            "\"price\" = :price " .
            "WHERE \"article_id\" = :article_id")->andReturn($this->statement);
        $this->statement->execute(new Pageley_Util_ArrayMap(array(
            "name" => "Llama spray",
            "price" => 5.5,
            "article_id" => 1)));
        $this->transaction->getSingleStatement()->andReturn(false);
        
        // Replay
        $this->mockFactory->replay();
        $entity = $this->type->newInstance();
        $entity->setId(1);
        $entity->set("name", "Llama spray");
        $entity->set("price", 5.5);
        $entity = $this->table->update($entity);
    }
    
    public function testDelete() {
        // Prepare scenario
        $this->gateway->getTransaction(true, true)->andReturn($this->transaction);
        $this->transaction->getConnection()->andReturn($this->connection);
        $this->connection->prepareStatement("DELETE FROM \"commerce\".\"article\" " .
            "WHERE \"article_id\" = :article_id")->andReturn($this->statement);
        $this->statement->execute(new Pageley_Util_ArrayMap(array("article_id" => 1)));
        $this->transaction->getSingleStatement()->andReturn(false);
        
        // Replay
        $this->mockFactory->replay();
        $entity = $this->type->newInstance();
        $entity->setId(1);
        $entity->set("name", "Llama spray");
        $entity->set("price", 5.5);
        $entity = $this->table->delete($entity);
    }
}